﻿using System;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using SocketControl.Iocp.Buffer;
using SocketControl.Iocp.Pool;
using SocketControl.Log;
using System.Text;

namespace SocketControl.Iocp
{
    public class Server
    {
        #region Socket定义
        private System.Net.Sockets.Socket _mSocket = null;
        #endregion

        #region 基本属性定义
        private int _mRecvBuffer; //接收数据缓存
        private readonly Semaphore _mSemaphore;//连接信号量;
        private int _mTotalBytesRead;//读取数据包总长度;
        private int _mNumConnectedSockets;//当前连接数;

        /// <summary>
        /// 设置或获取是否回包
        /// </summary>
        public bool IsBackParket = true;
        #endregion

        #region PUBLIC
        /// <summary>
        /// 获取当前连接数；
        /// </summary>
        public int GetConnectedNum { get { return _mNumConnectedSockets; } }
        #endregion

        #region

        private const int OpsToPreAlloc = 2;//??
        private readonly SocketAsyncEventArgsPool _mAsyncEventArgsPool;//消息线程池；
        #endregion

        #region Event
        public EventHandler<SocketAsyncEventArgs> OnAcceptCompleted;
        public EventHandler<SocketAsyncEventArgs> OnRecvCompleted;
        public EventHandler<SocketAsyncEventArgs> OnSendCompleted;
        public EventHandler<SocketAsyncEventArgs> OnClose;
        public EventHandler<SocketAsyncEventArgs> OnConnectionSuccess;
        #endregion

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="numConnections"></param>
        /// <param name="bufferSize"></param>
        public Server(int numConnections, int bufferSize)
        {
            _mTotalBytesRead = 0;
            _mNumConnectedSockets = 0;
            _mRecvBuffer = bufferSize;
            // 建立缓存对象
            var mBufferManager = new BufferManager(bufferSize * numConnections * OpsToPreAlloc,
                bufferSize);
            //建立线程池
            _mAsyncEventArgsPool = new SocketAsyncEventArgsPool(numConnections);
            _mSemaphore = new Semaphore(numConnections, numConnections);
            //---------------------------------------------------------
            // 初始化缓存
            mBufferManager.InitBuffer();
            for (int i = 0; i < numConnections; i++)
            {
                //实例化SocketAsyncEventArgs对象
                var readWriteEventArg = new SocketAsyncEventArgs();
                readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                readWriteEventArg.UserToken = new AsyncUserToken();

                // SocketAsyncEventArgs 分配缓存；
                mBufferManager.SetBuffer(readWriteEventArg);

                // SocketAsyncEventArgs放入线程池
                _mAsyncEventArgsPool.Push(readWriteEventArg);
            }
        }

        private void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    {
                        ProcessReceive(e);
                        break;
                    }
                case SocketAsyncOperation.Send:
                    {
                        ProcessSend(e);
                        break;
                    }
                case SocketAsyncOperation.Connect:
                    {
                        ProcessConnection(e);
                        break;
                    }
                default:
                    {
                        break;
                    }
            }
        }

        /// <summary>
        /// 绑定一个主机端口
        /// </summary>
        /// <param name="ip">主机IP</param>
        /// <param name="port">端口号</param>
        public void Bind(string ip, int port)
        {
            //TODO
            try
            {
                var ipAddress = new IPEndPoint(IPAddress.Parse(ip), port);
                _mSocket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _mSocket.Bind(ipAddress);
            }
            catch (Exception e)
            {
                LogHelper.WriteError(this.GetType(), e);
            }
        }

        /// <summary>
        /// 监听
        /// </summary>
        /// <param name="backlog">最大监听数</param>
        public void Listen(int backlog = 100)
        {
            try
            {
                _mSocket.Listen(backlog);
                LogHelper.WriteInfo(this.GetType(), "test");
            }
            catch (Exception e)
            {
                LogHelper.WriteError(this.GetType(), e);
            }
        }

        /// <summary>
        /// 接收连接
        /// </summary>
        /// <param name="acceptEventArgs">SocketAsyncEventArgs</param>
        public void Accept(SocketAsyncEventArgs acceptEventArgs)
        {
            try
            {
                if (acceptEventArgs == null)
                {
                    acceptEventArgs = new SocketAsyncEventArgs();
                    acceptEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
                }
                else
                {
                    // socket must be cleared since the context object is being reused
                    acceptEventArgs.AcceptSocket = null;
                }
                _mSemaphore.WaitOne();
                bool willRaiseEvent = _mSocket.AcceptAsync(acceptEventArgs);
                if (!willRaiseEvent)
                {
                    ProcessAccept(acceptEventArgs);
                }
            }
            catch (Exception e)
            {
                LogHelper.WriteError(this.GetType(), e);
                CloseSocket(acceptEventArgs);
            }
        }

        private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
        {
            if (OnAcceptCompleted != null)
            {
                try
                {
                    OnAcceptCompleted(this, e);
                }
                catch (Exception evente)
                {
                    LogHelper.WriteError(this.GetType(), evente);
                }
            }
            ProcessAccept(e);
        }

        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            try
            {
                Interlocked.Increment(ref _mNumConnectedSockets);
                //把弹出一个栈里的对象；
                SocketAsyncEventArgs readEventArgs = null;
                readEventArgs = _mAsyncEventArgsPool.Pop();
                ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket;
                //如果当前连接处理连接状态并发送了信息进入接收消息；
                bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);

                if (!willRaiseEvent)
                {
                    ProcessReceive(readEventArgs);
                }
                //等待下一个连接
                Accept(e);
            }
            catch (Exception accepte)
            {
                LogHelper.WriteError(this.GetType(), accepte);
                CloseSocket(e);
            }
        }

        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            try
            {
                var token = (AsyncUserToken)e.UserToken;
                if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                {
                    //increment the count of the total bytes receive by the server
                    Interlocked.Add(ref _mTotalBytesRead, e.BytesTransferred);

                    if (OnRecvCompleted != null)
                    {
                        try
                        {
                            OnRecvCompleted(this, e);
                        }
                        catch (Exception evente)
                        {
                            LogHelper.WriteError(this.GetType(), evente);
                        }
                    }
                    if (IsBackParket)
                    {
                        if (e.SocketError == SocketError.Success)
                        {
                            Send(e);
                        }
                    }
                    else
                    {
                        if (e.SocketError == SocketError.Success)
                        {
                            if (token.Socket.Connected)
                            {
                                bool willRaiseEvent = token.Socket.ReceiveAsync(e);
                                if (!willRaiseEvent)
                                {
                                    ProcessReceive(e);
                                }
                            }
                        }
                        else
                        {
                            CloseSocket(e);
                        }
                    }
                }
                else
                {
                    CloseSocket(e);
                }
            }
            catch (Exception recve)
            {
                LogHelper.WriteError(this.GetType(), recve);
                CloseSocket(e);
            }
        }

        private void ProcessSend(SocketAsyncEventArgs e)
        {
            try
            {
                LogHelper.WriteInfo(this.GetType(), "\r\n" + Encoding.Default.GetString(e.Buffer));
                var token = (AsyncUserToken)e.UserToken;
                if (e.SocketError == SocketError.Success)
                {
                    if (OnSendCompleted != null)
                    {
                        try
                        {
                            OnSendCompleted(this, e);
                        }
                        catch (Exception evente)
                        {
                            LogHelper.WriteError(this.GetType(), evente);
                        }
                    }
                    if (e.SocketError == SocketError.Success)
                    {
                        if (token.Socket.Connected)
                        {
                            bool willRaiseEvent = token.Socket.ReceiveAsync(e);
                            if (!willRaiseEvent)
                            {
                                ProcessReceive(e);
                            }
                        }
                    }
                }
                else
                {
                    CloseSocket(e);
                }
            }
            catch (Exception sende)
            {
                LogHelper.WriteError(this.GetType(), sende);
                CloseSocket(e);
            }
        }

        public void Send(SocketAsyncEventArgs e)
        {
            try
            {
                if (e != null && e.SocketError == SocketError.Success)
                {
                    var token = (AsyncUserToken)e.UserToken;
                    bool willRaiseEvent = token.Socket.SendAsync(e);
                    if (!willRaiseEvent)
                    {
                        ProcessSend(e);
                    }
                }
                else
                {
                    CloseSocket(e);
                }
            }
            catch (Exception sende)
            {
                LogHelper.WriteError(this.GetType(), sende);
                CloseSocket(e);
            }
        }

        public void Connection(string ip, int port)
        {
            try
            {
                var mConn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                var cArgs = _mAsyncEventArgsPool.Pop();
                cArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
                ((AsyncUserToken)cArgs.UserToken).Socket = mConn;
                bool willRaiseEvent = mConn.ConnectAsync(cArgs);
                if (!willRaiseEvent)
                {
                    ProcessConnection(cArgs);
                }
            }
            catch (Exception connectione)
            {
                LogHelper.WriteError(this.GetType(), connectione);
            }
        }

        public void ProcessConnection(SocketAsyncEventArgs e)
        {
            try
            {
                Interlocked.Increment(ref _mNumConnectedSockets);
                _mSemaphore.WaitOne();
                if (e.SocketError == SocketError.Success)
                {
                    if (OnConnectionSuccess != null)
                    {
                        try
                        {
                            OnConnectionSuccess(this, e);
                        }
                        catch (Exception evente)
                        {
                            LogHelper.WriteError(this.GetType(), evente);
                        }
                    }
                }
                else
                {
                    CloseSocket(e);
                }
            }
            catch (Exception connectione)
            {
                LogHelper.WriteError(this.GetType(), connectione);
                CloseSocket(e);
            }
        }

        /// <summary>
        /// 关闭一个连接
        /// </summary>
        /// <param name="e">SocketAsyncEventArgs</param>
        public void CloseSocket(SocketAsyncEventArgs e)
        {
            if (e != null)
            {
                var token = e.UserToken as AsyncUserToken;
                if (OnClose != null)
                {
                    OnClose(this, e);
                }
                // close the socket associated with the client
                try
                {
                    if (token != null) token.Socket.Shutdown(SocketShutdown.Send);
                }
                // throws if client process has already closed
                catch (Exception ex)
                {
                    ;
                }
                if (token != null) token.Socket.Close();
                // decrement the counter keeping track of the total number of clients connected to the server
                Interlocked.Decrement(ref _mNumConnectedSockets);

                _mSemaphore.Release();
                // Free the SocketAsyncEventArg so they can be reused by another client
                _mAsyncEventArgsPool.Push(e);
            }
        }
    }
}
